import functools
from contextlib import contextmanager

from typing import Dict

from ctaf_core.ctaf_objects.base_exception import CTAFbaseException
from ctaf_core.data_access_bundle.db_session_factory import DataBaseSessionFactory
from ctaf_facade.enumerations.error_code_enum import ErrorCodeEnum

"""
model： 为继承于Base的一个数据库表定义类
model_instance：为model的一个实例，即一条数据记录
"""
class DatabaseAccessTemplate:

    @staticmethod
    def insert(model_instance, propagational_session=None):
        #如果propagational_session不为空，则表示当前数据库操作需嵌入在其它事务范围内
        if propagational_session:
            propagational_session.add(model_instance)
        #如果propagational_session为空，则表示当前数据库操作需要创建独立事务，事务会在with块结束时自动提交/回滚
        else:
            with transaction_template() as local_session:
                local_session.add(model_instance)

    @staticmethod
    def delete(model, conditions=None, propagational_session=None):
        #如果propagational_session不为空，则表示当前数据库操作需嵌入在其它事务范围内
        if propagational_session:
            if conditions:
                propagational_session.query(model).filter(*conditions).delete()
            else:
                propagational_session.query(model).delete()
        #如果propagational_session为空，则表示当前数据库操作需要创建独立事务，事务会在with块结束时自动提交/回滚
        else:
            with transaction_template() as local_session:
                if conditions:
                    local_session.query(model).filter(*conditions).delete()
                else:
                    local_session.query(model).delete()

    @staticmethod
    def update(model, target: Dict, conditions=None , propagational_session=None):
        #如果propagational_session不为空，则表示当前数据库操作需嵌入在其它事务范围内
        if propagational_session:
            if conditions:
                propagational_session.query(model).filter(*conditions).update(target)
            else:
                propagational_session.query(model).update(target)
        #如果propagational_session为空，则表示当前数据库操作需要创建独立事务，事务会在with块结束时自动提交/回滚
        else:
            with transaction_template() as local_session:
                if conditions:
                    local_session.query(model).filter(*conditions).update(target)
                else:
                    local_session.query(model).update(target)

    @staticmethod
    def merge(model_instance, propagational_session=None):
        #如果propagational_session不为空，则表示当前数据库操作需嵌入在其它事务范围内
        if propagational_session:
            propagational_session.merge(model_instance)
        #如果propagational_session为空，则表示当前数据库操作需要创建独立事务，事务会在with块结束时自动提交/回滚
        else:
            with transaction_template() as local_session:
                local_session.merge(model_instance)

    @staticmethod
    def query(model_instance, conditions=None, propagational_session=None):

        def find_result(model, session, filters=None):
            query_result = session.query(model)
            if filters:
                query_result = query_result.filter(*filters)
            final_results = query_result.all()
            return final_results

        #如果propagational_session不为空，则表示当前session做查询
        if propagational_session:
            return find_result(model_instance, propagational_session, conditions)
        #如果propagational_session为空，则创建查询session，并在查询结束后，关闭session
        else:
            local_session = DataBaseSessionFactory.get_db_session()
            try:
                return find_result(model_instance, local_session, conditions)
            except Exception as e:
                raise CTAFbaseException(ErrorCodeEnum.DB_ACCESS_ERROR)
            finally:
                local_session.close()


    @staticmethod
    def execute_sql(sql, bind_variables=None, propagational_session=None):
        #如果propagational_session不为空，则表示当前数据库操作需嵌入在其它事务范围内
        if propagational_session:
            propagational_session.execute(sql, bind_variables)
        #如果propagational_session为空，则表示当前数据库操作需要创建独立事务，事务会在with块结束时自动提交/回滚
        else:
            with transaction_template() as local_session:
                local_session.execute(sql, bind_variables)

@contextmanager
def transaction_template():
    """事务模板上下文管理器"""
    session = DataBaseSessionFactory.get_db_session()
    try:
        yield session
        session.commit()
    except Exception as e:
        session.rollback()
        raise CTAFbaseException(ErrorCodeEnum.DB_ACCESS_ERROR)
    finally:
       session.close()

def transactional(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        session = DataBaseSessionFactory.get_db_session()
        try:
            result = func(*args, **kwargs, session= session)
            session.commit()
            return result
        except Exception as e:
            session.rollback()
            raise CTAFbaseException(ErrorCodeEnum.DB_ACCESS_ERROR)
        finally:
            session.close()

    return wrapper